/*
 * HNode.cpp
 *
 *  Created on: Apr 30, 2011
 *      Author: Matthew Michelotti
 */

#include "HNode.h"
#include <iostream>

using namespace std;

void HNode::makeNodes(matrix_t& matrix, vector<HNode*>& buffer) {
	for(uint i = 0; i < matrix.num_rows; i++) {
		buffer.push_back(new HNode(true));
	}
	for(uint i = 0; i < matrix.num_rows; i++) {
		HNode* node1 = buffer[i];
		int* neighbors = matrix.getNZCols(i);
		int len = matrix.getNNZs(i);
		for(int j = 0; j < len; j++) {
			node1->connections.push_back(buffer[neighbors[j]]);
		}
	}
}

HNode* HNode::formParent(const vector<HNode*>& children) {
	HNode* parent = new HNode(false);
	parent->connections = children;
	for(uint i = 0; i < children.size(); i++) {
		children[i]->parent = parent;
		parent->size += children[i]->size;
	}
	return parent;
}

void HNode::toMatrix(const vector<HNode*>& nodes, matrix_t& matrix) {
	set<HNode*> nodeSet;
	for(uint i = 0; i < nodes.size(); i++) nodeSet.insert(nodes[i]);

	map<HNode*, int> maps[nodes.size()];
	map<HNode*, int> indexMap;
	int nnz = 0;
	for(uint i = 0; i < nodes.size(); i++) {
		nodes[i]->getNeighbors(nodeSet, maps[i], nodes[i]);
		nnz += maps[i].size();
		indexMap[nodes[i]] = i;
	}

	matrix = matrix_t(nodes.size(), nodes.size(), nnz);
	int matrixIndex = 0;
	for(uint row = 0; row < matrix.num_rows; row++) {
		matrix.row_offsets[row] = matrixIndex;
		map<int, int> indexToWeightMap;
		for(map<HNode*, int>::iterator it = maps[row].begin(); it != maps[row].end(); it++) {
			indexToWeightMap[indexMap[it->first]] = it->second;
		}
		for(map<int, int>::iterator it = indexToWeightMap.begin(); it != indexToWeightMap.end(); it++) {
			matrix.column_indices[matrixIndex] = it->first;
			matrix.values[matrixIndex] = it->second;
			matrixIndex++;
		}
	}
	matrix.row_offsets[matrix.num_rows] = matrixIndex;
}

void HNode::split(vector<HNode*>& buffer, int i) {
	HNode* node = buffer[i];
	if(node->isLeaf) return;

	buffer[i] = buffer.back();
	buffer.pop_back();
	for(uint i = 0; i < node->connections.size(); i++) {
		buffer.push_back(node->connections[i]);
	}
}

void HNode::getNeighbors(const set<HNode*>& candidates, map<HNode*, int>& result, const HNode* caller) {
	if(!isLeaf) {
		for(uint i = 0; i < connections.size(); i++) {
			connections[i]->getNeighbors(candidates, result, caller);
		}
		return;
	}

	for(uint i = 0; i < connections.size(); i++) {
		HNode* neighbor = connections[i];
		while(candidates.count(neighbor) == 0 && neighbor->parent != 0) {
			neighbor = neighbor->parent;
		}
		if(neighbor == caller) continue;
		if(candidates.count(neighbor) == 0) continue;

		if(result.count(neighbor) == 0) result[neighbor] = 1;
		else result[neighbor]++;
	}
}

void HNode::print() const {
	cout << "HNode:" << endl;
	cout << "  Address: " << this << endl;
	cout << "  Type: " << (isLeaf ? "leaf" : "parent") << endl;
	cout << "  Size: " << size << endl;
	cout << "  Parent: " << parent << endl;
	cout << "  " << (isLeaf ? "connections" : "children") << ":" << endl;
	for(uint i = 0; i < connections.size(); i++) {
		cout << "    " << connections[i] << endl;
	}
}

